package agentd_web

import (
	"log"
	"os"
	"os/exec"
	"runtime"
	"time"

	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"wiseverds.com/frame/webport/src/service/agentd_mux1"
)

func sendResult(c *gin.Context, status int, res interface{}) {
	c.JSON(status, gin.H{
		"result": res,
	})
}

func sendError(c *gin.Context, status int, err interface{}) {
	c.JSON(status, gin.H{
		"error": err,
	})
}

// GET /api/test/ping
func handleTestPing(c *gin.Context) {
	sendResult(c, 200, "pong")
}

// GET /api/test/echo
func handleTestEcho(c *gin.Context) {
	message := c.DefaultQuery("message", "no message")
	sendResult(c, 200, message)
}

// GET /api/test/sleep
func handleTestSleep(c *gin.Context) {
	var params struct {
		Time float64 `form:"time" json:"time" binding:"required"`
	}

	if err := c.ShouldBind(&params); err != nil {
		sendError(c, 400, err.Error())
		return
	}

	timeMS := int(params.Time * 1000)
	time.Sleep(time.Duration(timeMS) * time.Millisecond)

	sendResult(c, 200, "success")
}

// GET /api/status/online
func handleStatusOnline(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// Stop this process.
// GET /api/control/stop
func handleControlStop(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// Restart this process.
// GET /api/control/restart
func handleControlRestart(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// Get WebSocket client list.
// GET /api/wsclient/list
func handleWsclientList(c *gin.Context) {
	list := agentd_mux1.DefaultServer.Proxy.GetWSClientList()

	type WSClient struct {
		Number  uint32
		ConnID  string
		Service string
	}

	result := make([]WSClient, 0)

	for _, item := range list {
		result = append(result, WSClient{
			Number:  item.Number,
			ConnID:  item.ConnID,
			Service: item.Service,
		})
	}

	log.Printf("%v", result)
	sendResult(c, 200, result)
}

// Get system type.
// GET /api/system/type
func handleSystemType(c *gin.Context) {
	sendResult(c, 200, runtime.GOOS)
}

// Get current user name.
// GET /api/system/user
func handleSystemUser(c *gin.Context) {
	user := os.Getenv("USER")
	sendResult(c, 200, user)
}

// Get current system time as unix timestamp.
// GET /api/system/time/unix
func handleSystemTimeUnix(c *gin.Context) {
	time := time.Now().Unix()
	sendResult(c, 200, time)
}

// Get the stat info of a dir entry.
// GET /api/fs/stat
func handleFsStat(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// Check if the dir entry is a directory.
// GET /api/fs/is_dir
func handleFsIsDir(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// Check if the dir entry is a regular file.
// GET /api/fs/is_file
func handleFsIsFile(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// POST /api/fs/chmod
func handleFsChmod(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// GET /api/fs/list_dir
func handleFsListDir(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// POST /api/fs/make_dir
func handleFsMakeDir(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// POST /api/fs/remove_dir
func handleFsRemoveDir(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// GET /api/fs/read_file
func handleFsReadFile(c *gin.Context) {
	path := c.DefaultQuery("path", "")
	if path == "" {
		sendError(c, 400, "path is empty")
		return
	}

	// check if the file exists and readable
	_, err := os.Stat(path)
	if err != nil {
		if os.IsNotExist(err) {
			sendError(c, 404, "file not found")
			return
		} else if os.IsPermission(err) {
			sendError(c, 403, "permission denied")
			return
		} else {
			sendError(c, 500, err.Error())
			return
		}
	}

	c.File(path)
}

// POST /api/fs/write_file
func handleFsWriteFile(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// POST /api/fs/write_file_chunk
func handleFsWriteFileChunk(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// POST /api/fs/append_file
func handleFsAppendFile(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// POST /api/fs/remove_file
func handleFsRemoveFile(c *gin.Context) {
	sendError(c, 500, "not implemented yet")
}

// POST /api/shell/open_file
func handleShellOpenFile(c *gin.Context) {
	var params struct {
		Path   string `form:"path" json:"path" binding:"required"`
		Opener string `form:"opener" json:"opener" binding:"-"`
	}

	if err := c.ShouldBind(&params); err != nil {
		sendError(c, 400, err.Error())
		return
	}

	// check if the file exists and readable
	_, err := os.Stat(params.Path)
	if err != nil {
		if os.IsNotExist(err) {
			sendError(c, 404, "file not found")
			return
		} else if os.IsPermission(err) {
			sendError(c, 403, "permission denied")
			return
		} else {
			sendError(c, 500, err.Error())
			return
		}
	}

	// set default opener
	if params.Opener == "" {
		switch runtime.GOOS {
		case "windows":
			params.Opener = "explorer"
		case "linux":
			params.Opener = "xdg-open"
		case "darwin":
			params.Opener = "open"
		default:
			sendError(c, 500, "unsupported os")
			return
		}
	}

	// open the file
	cmd := exec.Command(params.Opener, params.Path)
	log.Printf("open file: %v", params)
	err = cmd.Start()
	if err != nil {
		sendError(c, 500, err.Error())
		return
	}

	sendResult(c, 200, "success")
}

// POST /api/shell/exec_file
func handleShellExecFile(c *gin.Context) {
	sendResult(c, 500, "not implemented yet")
}

// POST /api/shell/exec_cmd
func handleShellExecCmd(c *gin.Context) {
	sendResult(c, 500, "not implemented yet")
}

// GET /api/ws/test/echo
func handleWsTestEcho(c *gin.Context) {
	upgrader := websocket.Upgrader{}
	ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		sendError(c, 500, err.Error())
		return
	}

	defer ws.Close()

	for {
		// read message
		mt, message, err := ws.ReadMessage()
		if err != nil {
			log.Println("read:", err)
			break
		}

		log.Printf("recv: %s", message)

		// write message
		err = ws.WriteMessage(mt, message)
		if err != nil {
			log.Println("write:", err)
			break
		}
	}
}

// GET /api/ws/shell/exec_file
func handleWsShellExecFile(c *gin.Context) {
	// RFC6455: the method of a WebSocket request MUST be GET
	sendResult(c, 500, "not implemented yet")
}
